package com.nageoffer.shortlink.admin.common.biz.user;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.nageoffer.shortlink.admin.common.convention.exception.ClientException;
import com.nageoffer.shortlink.admin.common.convention.result.Results;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;

import static com.nageoffer.shortlink.admin.common.enums.UserErrorCodeEnum.USER_TOKEN_FALL;

/**
 * 用户信息传输过滤器，用于从请求头中获取用户信息（如用户名和 token），
 * 然后从 Redis 中获取相应的用户信息，并将其放入当前线程的上下文中，以供后续处理使用。
 */
@RequiredArgsConstructor  // 通过构造函数注入依赖的 StringRedisTemplate
public class UserTransmitFilter implements Filter {

    private final StringRedisTemplate stringRedisTemplate;  // 用于访问 Redis

    private static final List<String> IGNORE_URI = Lists.newArrayList(
      "/api/short-link/admin/v1/user/login",
            "/api/short-link/admin/v1/user/has-username",
            "/api/short-link/admin/v1/user"
    );


    @SneakyThrows
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String requestURI = httpServletRequest.getRequestURI();
        if(!IGNORE_URI.contains(requestURI)) {///不存在于我们的白名单中，那么就需要去认证
            String method = httpServletRequest.getMethod();
            boolean NeedToYanZheng = Objects.equals(method, "POST")&&Objects.equals(requestURI, "/api/short-link/admin/v1/user");
            if(!NeedToYanZheng) {///同时还得验证"/api/short-link/admin/v1/user"是否是POST方法
                String username = httpServletRequest.getHeader("username");
                String token = httpServletRequest.getHeader("token");

                if (!StrUtil.isAllNotBlank(username, token)) {
                    ///引入错误码需要在前面写下具体的import：import static com.nageoffer.shortlink.admin.common.enums.UserErrorCodeEnum.USER_TOKEN_FALL;
                    //throw new ClientException(USER_TOKEN_FALL);
                    returnJson((HttpServletResponse) servletResponse, JSON.toJSONString(Results.failure(new ClientException(USER_TOKEN_FALL))));
                    return;
                }

                // 从 Redis 中获取与指定用户名（login_ + username）和 token 对应的用户信息
                // 使用 StringRedisTemplate 的 opsForHash 方法，查询哈希表中存储的用户信息
                Object userInfoJsonStr;
                try {
                    // 将 Redis 中存储的 JSON 字符串解析为 UserInfoDTO 对象
                    userInfoJsonStr = stringRedisTemplate.opsForHash().get("login_" + username, token);
                    if (userInfoJsonStr == null) {
                        throw new ClientException(USER_TOKEN_FALL);
                    }
                } catch (Exception ex) {
                    returnJson((HttpServletResponse) servletResponse, JSON.toJSONString(Results.failure(new ClientException(USER_TOKEN_FALL))));
                    return;
                }
                UserInfoDTO userInfoDTO = JSON.parseObject(userInfoJsonStr.toString(), UserInfoDTO.class);
                UserContext.setUser(userInfoDTO);

            }
        }
        try {
            // 继续执行请求链，传递给下一个过滤器或目标资源
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            // 不管请求是否处理成功，最终都会清理用户上下文，避免信息泄漏
            UserContext.removeUser();
        }
    }

    private void returnJson(HttpServletResponse response, String json) throws Exception {
        PrintWriter writer =null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/json; charset=utf-8");
        try{
            writer =response.getWriter();
            writer.print(json);
        }catch (Exception ex){
        }finally {
            if(writer != null){
                writer.close();
            }
        }
    }

}